package com.qianli.cashmama.trade.repo.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fqgj.common.utils.CollectionUtils;
import com.fqgj.common.utils.StringUtils;
import com.fqgj.exception.common.ApplicationException;
import com.google.common.base.CharMatcher;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.qianli.cashmama.trade.common.enums.TradeAuditSubStatusEnum;
import com.qianli.cashmama.trade.common.enums.TradeSignStatusEnum;
import com.qianli.cashmama.trade.common.enums.TradeStatusEnum;
import com.qianli.cashmama.trade.common.error.TradeErrorCodeEnum;
import com.qianli.cashmama.trade.common.exceptions.TradeException;
import com.qianli.cashmama.trade.dao.*;
import com.qianli.cashmama.trade.domain.event.TradeFlow;
import com.qianli.cashmama.trade.domain.model.TradeBank;
import com.qianli.cashmama.trade.domain.model.TradeBase;
import com.qianli.cashmama.trade.domain.model.TradeDiscount;
import com.qianli.cashmama.trade.domain.model.TradeRisk;
import com.qianli.cashmama.trade.entity.*;
import com.qianli.cashmama.trade.repo.TradeBaseRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * Created by scrack on 2017/7/20.
 */

@Service
public class TradeBaseRepoImpl implements TradeBaseRepository {
    private static final Logger LOGGER = LoggerFactory.getLogger(TradeBaseRepoImpl.class);

    @Autowired
    private TTradeDao tradeDao;

    @Autowired
    private TRiskInfoDao riskInfoDao;

    @Autowired
    private TBankInfoDao bankInfoDao;

    @Autowired
    private TTradeFlowDao tTradeFlowDao;

    @Autowired
    private TDiscountDao discountDao;

    @Autowired
    private TTradeContractDao tradeContractDao;

    /**
     * TradeBase tradeBase 是个聚合根,聚合其它的实体类:比如合同,风控信息,折扣信息等，
     * 通过聚合根tradeBase来访问其它所聚合的实体类
     * 仓储针对聚合根和他的属性对象是整存整取
     */
    @Override
    public void store(TradeBase tradeBase) {
        try {
            storeTrade(tradeBase);
            storeTradeRiskInfo(tradeBase);
            storeTradeBankInfo(tradeBase);
            storeContractInfo(tradeBase);
//            storeTradeDiscountInfo(tradeBase);
        } catch (Exception e) {
            LOGGER.error("==存储交易失败==,tradeNo:{},e", tradeBase.getTradeNo(), e);
            throw new TradeException(TradeErrorCodeEnum.trade_not_exist);
        }
    }

    @Override
    public void restore(TradeBase tradeBase) {
        try {
            restoreTrade(tradeBase);
            restoreTradeRiskInfo(tradeBase);
            restoreTradeBankInfo(tradeBase);
            restoreContractInfo(tradeBase);
//            restoreTradeDiscountInfo(tradeBase);
        } catch (Exception e) {
            LOGGER.error("==更新交易失败==,userCode:{},tradeNo:{},e", tradeBase.getUserCode(), tradeBase.getTradeNo(), e);
            throw new TradeException(TradeErrorCodeEnum.trade_not_exist);
        }
    }

    @Override
    public TradeBase load(String tradeNo) {
        try {
            //装载交易
            TradeBase tradeBase = loadTrade(tradeNo);

            if (tradeNo.contains("TD")) {
                return tradeBase;
            }

            //装载银行卡信息
            loadBank(tradeBase, tradeNo);

            //装载优惠信息
            loadDiscount(tradeBase, tradeNo);

            //装载合同信息
            loadContractInfo(tradeBase, tradeNo);
            return tradeBase;
        } catch (Exception e) {
            LOGGER.info("==load 交易失败 ==,tradeNo:{},e", tradeNo, e);
            return null;
        }
    }

    @Override
    public TradeBase loadWithRisk(String tradeNo) {
        try {
            //装载交易
            TradeBase tradeBase = loadTrade(tradeNo);

            //装载风控信息 //
            loadRisk(tradeBase, tradeNo);

            //装载银行卡信息
            loadBank(tradeBase, tradeNo);

            //装载优惠信息
            loadDiscount(tradeBase, tradeNo);

            //装载合同信息
            loadContractInfo(tradeBase, tradeNo);

            return tradeBase;
        } catch (TradeException e) {
            LOGGER.info("==load 交易失败==,e,tradeNo:{}", e, tradeNo);
            throw new TradeException(TradeErrorCodeEnum.trade_not_exist);
        }
    }

    /**
     * load交易 开始
     */
    private TradeBase loadTrade(String tradeNo) {
        TTradeEntity tTradeEntity = tradeDao.selectOneByTradeNo(tradeNo);
        LOGGER.info("==selectOneByTradeNo ,content ==tradeNo:{},tTradeEntity:{}", tradeNo, JSON.toJSONString(tTradeEntity));
        if (tTradeEntity == null) {
            LOGGER.info("==load交易失败 为空 ==tradeNo:{}", tradeNo);
            throw new TradeException(TradeErrorCodeEnum.trade_not_exist);
        }
        return convertEntityToTradeBase(tTradeEntity);
    }

    private void loadRisk(TradeBase tradeBase, String tradeNo) {
        try {
            List<TRiskInfoEntity> riskInfoEntityList = riskInfoDao.selectListByTradeNo(tradeNo);
            if (CollectionUtils.isEmpty(riskInfoEntityList)) {
                LOGGER.error("==  风控信息加载失败 为空==,tradeNo:{}", tradeNo);
                throw new TradeException(TradeErrorCodeEnum.risk_info_not_exist);
            }
            List<TradeRisk> tradeRisks = new ArrayList<>();
            for (TRiskInfoEntity entity : riskInfoEntityList) {
                TradeRisk tradeRisk = new TradeRisk()
                        .setValue(entity.getRiskValue())
                        .setUserSnapshotInfoTypeEnum(com.qianli.cashmama.trade.common.enums.UserSnapshotInfoTypeEnum.convertByType(entity.getRiskType()))
                        .setTradeNo(entity.getTradeNo());

                tradeRisks.add(tradeRisk);
            }

            tradeBase.setTradeRisks(tradeRisks);
        } catch (Exception e) {
            LOGGER.error("== load风控信息失败==,tradeNo:{},e", tradeNo, e);
            throw new TradeException(TradeErrorCodeEnum.trade_not_exist);
        }
    }

    private void loadBank(TradeBase tradeBase, String tradeNo) {
        try {
            TBankInfoEntity entity = bankInfoDao.selectOneByTradeNo(tradeNo);
            if (entity == null) {
                LOGGER.error("== 装载银行卡信息错误 为空==,tradeNo:{}", tradeNo);
                throw new TradeException(TradeErrorCodeEnum.trade_not_exist);
            }

            tradeBase.setTradeBank(new TradeBank()
                    .setSignMobile(entity.getSignMobile())
                    .setCardNo(entity.getCardNo())
                    .setTradeNo(entity.getTradeNo())
                    .setBankType(entity.getBankType())
                    .setBankName(entity.getBankName())
                    .setBindType(entity.getBindType())
                    .setIdentityNo(entity.getIdentityNo())
                    .setUserName(entity.getUserName()));
        } catch (Exception e) {
            LOGGER.error("== 装载银行卡信息错误 ==,tradeNo:{},e", tradeNo, e);
            throw new TradeException(TradeErrorCodeEnum.trade_not_exist);
        }
    }

    private void loadDiscount(TradeBase tradeBase, String tradeNo) {
        try {
            List<TDiscountEntity> entityList = discountDao.selectListByTradeNo(tradeNo);
            if (CollectionUtils.isEmpty(entityList)) {
                return;
            }

            List<TradeDiscount> tradeDiscountList = new ArrayList<>();
            for (TDiscountEntity entity : entityList) {
                tradeDiscountList.add(new TradeDiscount()
                        .setDiscountCode(entity.getDiscountCode())
                        .setDiscountType(com.qianli.cashmama.trade.common.enums.TradeDiscountTypeEnum.convertByType(entity.getType()))
                        .setInfo(entity.getDetail())
                        .setStatus(com.qianli.cashmama.trade.common.enums.TradeDiscountStatusEnum.convertByType(entity.getStatus()))
                        .setTradeNo(entity.getTradeNo())
                );
            }

            tradeBase.setTradeDiscountList(tradeDiscountList);
        } catch (Exception e) {
            LOGGER.error("== 装载优惠信息错误 ==,tradeNo:{},e", tradeNo, e);
            throw new TradeException(TradeErrorCodeEnum.trade_not_exist);
        }
    }

    private void loadContractInfo(TradeBase tradeBase, String tradeNo) {
        try {
            TTradeContractEntity tradeContractEntity = tradeContractDao.selectOneByTradeNo(tradeNo);
            if (tradeContractEntity == null) {
                return;
            }
            tradeBase.setSignDate(tradeContractEntity.getSignDate());
            tradeBase.setContractUrl(tradeContractEntity.getContractUrl());
            tradeBase.setDocumentId(tradeContractEntity.getDocumentId());
        } catch (Exception e) {
            LOGGER.error("== 装载合同信息错误 ==,tradeNo:[{}{}],e[{}]", tradeNo, e);
            throw new TradeException(TradeErrorCodeEnum.trade_not_exist);
        }
    }


    private void storeTradeEvent(TradeBase tradeBase) {
        String operator = StringUtils.isEmpty(tradeBase.getOperator()) ? "system" : tradeBase.getOperator();

        List<TradeFlow> tradeFlows = tradeBase.getTradeFlows();
        if (CollectionUtils.isEmpty(tradeFlows)) {
            return;
        }

        for (TradeFlow tradeFlow : tradeFlows) {
            TTradeFlowEntity tTradeFlowEntity = new TTradeFlowEntity()
                    .setTradeNo(tradeBase.getTradeNo())
                    .setAction(tradeFlow.getAction())
                    .setSourceStatus(tradeFlow.getSourceStatus().getStatus())
                    .setTargetStatus(tradeFlow.getTargetStatus().getStatus())
                    .setOperatorDate(tradeFlow.getOperateDate())
                    .setNote(tradeFlow.getNote())
                    .setOperator(operator);

            tTradeFlowDao.insert(tTradeFlowEntity);
        }
        tradeBase.clearEvent();
    }

    private TradeBase convertEntityToTradeBase(TTradeEntity tTradeEntity) {
        try {
            return new TradeBase().setTradeId(tTradeEntity.getId())
                    .setTradeNo(tTradeEntity.getTradeNo())
                    .setProductCode(tTradeEntity.getProductCode())
                    .setProductCategory(tTradeEntity.getProductCategory())
                    .setUserCode(tTradeEntity.getUserCode())
                    .setChannelCode(tTradeEntity.getChannelCode())
                    .setCustomerCode(tTradeEntity.getCustomerCode())

                    .setTradeOrderStatus(TradeStatusEnum.convertByStatus(tTradeEntity.getOrderStatus()))
                    .setTradeSignStatus(TradeSignStatusEnum.convertByStatus(tTradeEntity.getSignStatus()))
                    .setTradeAuditStatus(com.qianli.cashmama.trade.common.enums.TradeAuditStatusEnum.covertByStatus(tTradeEntity.getAuditStatus()))
                    .setTradeAuditSubStatus(TradeAuditSubStatusEnum.getEnum(tTradeEntity.getAuditSubStatus()))
                    .setTradePayStatus(com.qianli.cashmama.trade.common.enums.TradePayStatusEnum.convertByStatus(tTradeEntity.getPayStatus()))
                    .setClientType(com.qianli.cashmama.trade.common.enums.TradeClientTypeEnum.getByType(tTradeEntity.getClientType()))

                    .setBorrowTotalCapital(tTradeEntity.getBorrowTotalCapital())
                    .setReceivedTotalCapital(tTradeEntity.getReceivedTotalCapital())
                    .setRepaymentTotalCapital(tTradeEntity.getRepaymentTotalCapital())
                    .setPaidTotalCapital(tTradeEntity.getPaidTotalCapital())
                    .setTotalServiceFee(tTradeEntity.getTotalServiceFee())

                    .setBorrowRatio(tTradeEntity.getBorrowRatio())
                    .setTotalPeriod(tTradeEntity.getTotalPeriod())
                    .setPaidPeriod(tTradeEntity.getPaidPeriod())
                    .setBorrowDuration(tTradeEntity.getBorrowDuration())

                    .setOverdueRatio(tTradeEntity.getOverdueRatio())
                    .setTotalOverdueServiceFee(tTradeEntity.getTotalOverdueServiceFee())
                    .setTotalOverdueFee(tTradeEntity.getTotalOverdueFee())
                    .setTradeOrderType(com.qianli.cashmama.trade.common.enums.TradeTypeEnum.conventByType(tTradeEntity.getOrderType()))

                    .setOverdueServiceFee(tTradeEntity.getOverdueServiceFee())
                    .setBorrowDuration(tTradeEntity.getBorrowDuration())
                    .setDiscountTotalCapital(tTradeEntity.getDiscountTotalCapital())
                    .setPeriodLength(tTradeEntity.getPeriodLength())

                    .setReceivedDate(tTradeEntity.getReceivedDate())
                    .setCreatedDate(tTradeEntity.getGmtCreate())
                    .setNewUser(tTradeEntity.getIsNewUser() == 1)
                    .setUsageOfLoan(com.qianli.cashmama.trade.common.enums.UsageOfLoanEnum.getByCode(tTradeEntity.getUsageOfLoan()))
                    .setPayOffDate(tTradeEntity.getPayOffDate())
                    .setAuditDate(tTradeEntity.getAuditDate())
                    .setExtraData(tTradeEntity.getExtraData())
                    .setCapitalCode(StringUtils.isEmpty(tTradeEntity.getCapitalCode()) ? null : Integer.valueOf(tTradeEntity.getCapitalCode()))
                    .setMerchantCode(tTradeEntity.getMerchantCode());
        } catch (Exception e) {
            LOGGER.error("== convertEntityToTradeBase 失败 ==", e);
            throw new TradeException(TradeErrorCodeEnum.trade_not_exist);
        }
    }

    /**
     * ####################### load交易 结束##############################################
     */


    /**
     * ####################### store交易 开始##############################################
     */
    private void storeTrade(TradeBase tradeBase) {
        TTradeEntity tTradeEntity = new TTradeEntity();
        BeanUtils.copyProperties(tradeBase, tTradeEntity);

//        tTradeEntity.setId(IdCenterUtil.getSequenceNo(com.qianli.cashmama.trade.common.enums.TradeBizCodeEnum.TRADE));
        tTradeEntity.setOrderStatus(tradeBase.getTradeOrderStatus().getStatus())
                .setAuditStatus(tradeBase.getTradeAuditStatus().getStatus())
                .setAuditSubStatus(tradeBase.getTradeAuditSubStatus().getStatus())
                .setPayStatus(tradeBase.getTradePayStatus().getStatus())
                .setClientType(tradeBase.getClientType().getType())
                .setPeriodLength(tradeBase.getPeriodLength())
                .setIsNewUser(tradeBase.isNewUser() ? 1 : 0)
                .setCreateDt(new Date());
        if (Objects.nonNull(tradeBase.getUsageOfLoan())) {
            tTradeEntity.setUsageOfLoan(tradeBase.getUsageOfLoan().getCode());
        }
        if (Objects.nonNull(tradeBase.getTradeOrderType())) {
            tTradeEntity.setOrderType(tradeBase.getTradeOrderType().getType());
        }
        tradeDao.insert(tTradeEntity);
        if (tTradeEntity.getId() == null) {
            throw new TradeException(TradeErrorCodeEnum.trade_not_exist);
        }

        storeTradeEvent(tradeBase);
    }

    private void storeTradeRiskInfo(TradeBase tradeBase) {
        if (tradeBase.getTradeRisks() == null) {
            return;
        }

        List<TradeRisk> tradeRisks = tradeBase.getTradeRisks();
        if (CollectionUtils.isEmpty(tradeRisks)) {
            return;
        }
        for (TradeRisk tradeRisk : tradeRisks) {
            JSONObject riskValue = JSONObject.parseObject(tradeRisk.getValue());
            String name = Joiner.on(" ").skipNulls().join(riskValue.getString("userFirstName"), riskValue.getString("userMiddleName"), riskValue.getString("userLastName"));
            TRiskInfoEntity riskInfoEntity = new TRiskInfoEntity();
            riskInfoEntity.setRiskType(tradeRisk.getUserSnapshotInfoTypeEnum().getType())
                    .setTradeNo(tradeBase.getTradeNo())
                    .setRiskValue(tradeRisk.getValue())
                    .setName(CharMatcher.WHITESPACE.collapseFrom(name, ' '))
                    .setEmail(Strings.nullToEmpty(riskValue.getString("userEmail")))
                    .setPhone(Strings.nullToEmpty(riskValue.getString("mobile")));
//            riskInfoEntity.setId(IdCenterUtil.getSequenceNo(com.qianli.cashmama.trade.common.enums.TradeBizCodeEnum.RISK_INFO));
            riskInfoDao.insert(riskInfoEntity);
            if (riskInfoEntity.getId() == null) {
                throw new ApplicationException("Failed to store risk control information");
            }
        }

    }

    private void storeTradeBankInfo(TradeBase tradeBase) {
        if (tradeBase.getTradeBank() == null) {
            return;
        }

        TBankInfoEntity bankInfoEntity = new TBankInfoEntity();
        BeanUtils.copyProperties(tradeBase.getTradeBank(), bankInfoEntity);
        bankInfoEntity.setTradeNo(tradeBase.getTradeNo())
                .setUserName(tradeBase.getTradeBank().getUserName())
                .setBindType(tradeBase.getTradeBank().getBindType());
//        bankInfoEntity.setId(IdCenterUtil.getSequenceNo(com.qianli.cashmama.trade.common.enums.TradeBizCodeEnum.BANK_INFO));
        bankInfoDao.insert(bankInfoEntity);

        if (bankInfoEntity.getId() == null) {
            throw new TradeException(TradeErrorCodeEnum.trade_not_exist);
        }
    }

    private void storeContractInfo(TradeBase tradeBase) {
        TTradeContractEntity tradeContractEntity = new TTradeContractEntity();
        tradeContractEntity.setTradeNo(tradeBase.getTradeNo());
        tradeContractEntity.setUserCode(tradeBase.getUserCode());
        tradeContractDao.insert(tradeContractEntity);
        if (tradeContractEntity.getId() == null) {
            throw new TradeException(TradeErrorCodeEnum.trade_not_exist);
        }
    }

    private void restoreContractInfo(TradeBase tradeBase) {
        if (tradeBase.getContractUrl() == null && tradeBase.getSignDate() == null) {
            return;
        }
        TTradeContractEntity tradeContractEntity = new TTradeContractEntity();
        tradeContractEntity.setTradeNo(tradeBase.getTradeNo());
        tradeContractEntity.setContractUrl(tradeBase.getContractUrl());
        tradeContractEntity.setUserCode(tradeBase.getUserCode());
        tradeContractEntity.setSignDate(tradeBase.getSignDate());
        int num = tradeContractDao.updateByTradeNo(tradeContractEntity);
        if (num <= 0) {
            throw new TradeException(TradeErrorCodeEnum.trade_not_exist);
        }
    }

    /**
     * ####################### store交易 结束##############################################
     */


    /**
     * restore交易 开始
     *
     * @param tradeBase
     */
    private void restoreTrade(TradeBase tradeBase) {
        TTradeEntity tTradeEntity = new TTradeEntity();
        BeanUtils.copyProperties(tradeBase, tTradeEntity);

        String capitalCode = null;
        if (tradeBase.getCapitalCode() != null) {
            capitalCode = String.valueOf(tradeBase.getCapitalCode());
        }
        tTradeEntity.setTradeNo(tradeBase.getTradeNo())
                .setOrderStatus(tradeBase.getTradeOrderStatus().getStatus())
                .setSignStatus(tradeBase.getTradeSignStatus().getStatus())
                .setAuditStatus(tradeBase.getTradeAuditStatus().getStatus())
                .setAuditSubStatus(tradeBase.getTradeAuditSubStatus().getStatus())
                .setPayStatus(tradeBase.getTradePayStatus().getStatus())
                .setClientType(tradeBase.getClientType().getType())
                .setOrderType(tradeBase.getTradeOrderType().getType())
                .setPeriodLength(tradeBase.getPeriodLength())
                .setCapitalCode(capitalCode);

        tradeDao.updateByTradeNo(tTradeEntity);

        storeTradeEvent(tradeBase);
    }

    private void restoreTradeRiskInfo(TradeBase tradeBase) {
        if (tradeBase.getTradeRisks() == null) {
            return;
        }

        List<TradeRisk> tradeRisks = tradeBase.getTradeRisks();
        if (CollectionUtils.isEmpty(tradeRisks)) {
            return;
        }
        for (TradeRisk tradeRisk : tradeRisks) {
            TRiskInfoEntity riskInfoEntity = new TRiskInfoEntity();
            riskInfoEntity.setRiskType(tradeRisk.getUserSnapshotInfoTypeEnum().getType())
                    .setTradeNo(tradeBase.getTradeNo())
                    .setRiskValue(tradeRisk.getValue());

            riskInfoDao.updateByTradeNoAndType(riskInfoEntity);
        }
    }

    private void restoreTradeBankInfo(TradeBase tradeBase) {
        TradeBank tradeBank = tradeBase.getTradeBank();
        if (tradeBank == null) {
            return;
        }

        TBankInfoEntity bankInfoEntity = new TBankInfoEntity();
        BeanUtils.copyProperties(tradeBase.getTradeBank(), bankInfoEntity);
        bankInfoEntity.setTradeNo(tradeBase.getTradeNo())
                .setUserName(tradeBase.getTradeBank().getUserName())
                .setBindType(tradeBase.getTradeBank().getBindType());

        bankInfoDao.updateByTradeNo(bankInfoEntity);
    }

    @Override
    public void updateTradeByTradeNo(String tradeNo, String channelCode) {
        TTradeEntity tTradeEntity = tradeDao.selectOneByTradeNo(tradeNo);
        if (tTradeEntity != null) {
            tTradeEntity.setChannelCode(channelCode);
            tradeDao.updateByPrimaryKey(tTradeEntity);
        }
    }

    /**
     * ####################### restore交易 结束##############################################
     */


}
